﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using SevenZip;
using System.IO;

namespace MyEmulators2
{
    class Extractor
    {
        const string GOODMERGE_EXTRACT_FOLDER = "Emulators2_GoodMerge_Extract_Folder";
        const string GOODMERGE_FILENAME_PREFIX = "Emulators2_GoodMerge_File_";

        SevenZipExtractor extractor = null;
        bool ready = false;
        string errorStr = "";
        public Extractor() 
        {
            initExtractor();
        }

        public void ReInit()
        {
            initExtractor();
        }

        void initExtractor()
        {
            try
            {
                SevenZipCompressor.SetLibraryPath(Options.Instance.GetStringOption("7zdllpath"));
                ready = true;
            }
            catch (Exception ex)
            {
                Logger.LogError(ex);
                errorStr = string.Format("Goodmerge error:\r\nError setting path to 7z.dll\r\n{0}", ex.Message);
            }
        }

        bool loadArchive(string archivePath)
        {
            try
            {
                extractor = new SevenZipExtractor(archivePath);
            }
            catch(Exception ex)
            {
                if (extractor != null)
                {
                    extractor.Dispose();
                    extractor = null;
                }
                Conf_GlobalSettings.Instance.ShowMPDialog("Goodmerge error:\r\nError extracting archive\r\n{0}", ex.Message);
                return false;
            }
            return true;
        }

        #region Singleton
        protected static Extractor instance = null;
        public static Extractor Instance
        {
            get
            {
                if (instance == null)
                    instance = new Extractor();
                return instance;
            }
        }
        #endregion

        public static bool DllConfigured()
        {
            return System.IO.File.Exists(Options.Instance.GetStringOption("7zdllpath"));
        }
        
        //If the selected emulator has goodmerge enabled this
        //function will be called for every archive OR rom selected
        //as the plugin currently cannot distinguish between the two
        //
        //If this function returns null the plugin will attempt to launch the item
        //as a rom
        public Game[] ViewGames(Game game)
        {
            if (!ready)
            {
                Conf_GlobalSettings.Instance.ShowMPDialog(errorStr);
                return null;
            }

            if (!loadArchive(game.Path))
                return null;

            List<Game> games = new List<Game>();

            Logger.LogInfo("Reading archive '{0}', found {1} file{2}", game.Filename, extractor.ArchiveFileNames.Count, extractor.ArchiveFileNames.Count == 1 ? "" : "s");


            if (extractor.ArchiveFileNames.Count == 1)
            {
                Logger.LogDebug("Found '{0}'", extractor.ArchiveFileNames[0]);
                games.Add(game);
            }
            else if (extractor.ArchiveFileNames.Count > 0)
            {
                for (int i = 0; i < extractor.ArchiveFileNames.Count; i++)
                {
                    Logger.LogDebug("Found {0}", extractor.ArchiveFileNames[i]);

                    Game RomListGame = game.Clone();
                    RomListGame.LaunchFile = extractor.ArchiveFileNames[i];
                    RomListGame.Title = RomListGame.LaunchFile;
                    games.Add(RomListGame);
                }
            }
            extractor.Dispose();
            return games.ToArray();
        }

        public string ExtractGame(Game game)
        {
            return ExtractGame(game, null);
        }

        public string ExtractGame(Game game, EmulatorProfile profile)
        {
            if (!ready)
            {
                Conf_GlobalSettings.Instance.ShowMPDialog(errorStr);
                return null;
            }

            EmulatorProfile emuProfile;
            if (profile != null && profile.EmulatorID == game.ParentEmulator.UID)
                emuProfile = profile;
            else
                emuProfile = game.GetSelectedProfile();

            loadArchive(game.Path);
            if (extractor == null)
                return null;

            if (extractor.ArchiveFileNames.Count == 0)
            {
                Logger.LogError("No files were found in archive '{0}'", game.Filename);
                Conf_GlobalSettings.Instance.ShowMPDialog("Goodmerge error:\r\nThe selected archive is empty");
                extractor.Dispose();
                return null;
            }

            string GoodmergeTempPath = getGoodmergePath();

            Logger.LogDebug("Selecting game to extract from archive '{0}'", game.Filename);
            int index = -1;

            if (extractor.ArchiveFileNames.Count > 1)
            {
                if (game.LaunchFile != "")
                {
                    //there should be a match so loop through first for speed
                    for (int x = 0; x < extractor.ArchiveFileNames.Count; x++)
                    {
                        if (game.LaunchFile == extractor.ArchiveFileNames[x])
                        {
                            index = x;
                            break;
                        }
                    }
                }

                //loop through all items in archive and select best match based on emulator settings
                if (index < 0)
                    index = getBestGoodmergeMatch(extractor, emuProfile);
            }

            if (index < 0)
                index = 0; //if no matches found, choose first file

            extractor.ExtractFiles(GoodmergeTempPath, extractor.ArchiveFileData[index].Index);

            string newPath = Path.Combine(GoodmergeTempPath, GOODMERGE_FILENAME_PREFIX + extractor.ArchiveFileNames[index]);

            try
            {
                File.Move(Path.Combine(GoodmergeTempPath, extractor.ArchiveFileNames[index]), newPath);
            }
            catch(Exception ex)
            {
                extractor.Dispose();
                Conf_GlobalSettings.Instance.ShowMPDialog("Goodmerge error:\r\nError creating temporary game file\r\n{0}", ex.Message);
                return null;
            }

            Logger.LogDebug("Selected '{0}' from archive '{1}'", extractor.ArchiveFileNames[index], game.Filename);
            extractor.Dispose();

            return newPath;
        }
        
        static string getGoodmergePath()
        {
            string GoodmergeTempPath = Path.Combine(System.IO.Path.GetTempPath(), GOODMERGE_EXTRACT_FOLDER);
            if (!Directory.Exists(GoodmergeTempPath))
                Directory.CreateDirectory(GoodmergeTempPath);
            else
                foreach (string s in System.IO.Directory.GetFiles(GoodmergeTempPath))
                {
                    try
                    {
                        System.IO.File.Delete(s);
                    }
                    catch { }
                }
            return GoodmergeTempPath;
        }

        static int getBestGoodmergeMatch(SevenZipExtractor extractor, EmulatorProfile emuProfile)
        {
            //loop through all items in archive and select best match based on emulator settings
            int priority = 7;
            int index = -1;
            for (int x = 0; x < extractor.ArchiveFileNames.Count; x++)
            {
                if (extractor.ArchiveFileNames[x].Contains(emuProfile.GoodMergePref1))
                {
                    if (extractor.ArchiveFileNames.Contains("[!]"))
                    {
                        index = x;
                        break; //highest priority so break and launch 
                    }
                    else if (priority > 2)
                    {
                        priority = 2;
                        index = x;
                    }
                    continue;
                }

                else if (priority > 3 && extractor.ArchiveFileNames[x].Contains(emuProfile.GoodMergePref2))
                {
                    if (extractor.ArchiveFileNames.Contains("[!]"))
                    {
                        priority = 3;
                        index = x;
                    }
                    else if (priority > 4)
                    {
                        priority = 4;
                        index = x;
                    }
                    continue;
                }

                else if (priority > 5 && extractor.ArchiveFileNames[x].Contains(emuProfile.GoodMergePref3))
                {
                    if (extractor.ArchiveFileNames.Contains("[!]"))
                    {
                        priority = 5;
                        index = x;
                    }
                    else if (priority > 6)
                    {
                        priority = 6;
                        index = x;
                    }
                    continue;
                }
            }

            return index;
        }

    }
}
